﻿using ArduinoAnalyzer.LexicalAnalysis;
using ArduinoAnalyzer.SemanticAnalysis;
using ArduinoAnalyzer.Tokens;
using ArduinoAnalyzer.Tokens.LexicalAnalysis;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Type = ArduinoAnalyzer.SemanticAnalysis.Type;

namespace ArduinoAnalyzer.SyntacticAnalysis
{
    public class SyntacticAnalyzer
    {
        #region Constructors

        public SyntacticAnalyzer(IEnumerator<Token> enumerator)
        {
            _enumerator = enumerator;
        }

        #endregion

        #region Private Members

        private IEnumerator<Token> _enumerator;
        private List<Token> _tokens = new List<Token>();
        private int _currentIndex = -1;
        private Token _current;
        private bool _end = false;
        private TypesTable _typesTable;
        private int _tempCount = 0;
        private int _labelsCount = 0;

        #endregion

        #region Attributes and Properties

        private List<SemanticError> _semanticErrors;
        public List<SemanticError> SemanticErrors
        {
            get { return _semanticErrors; }
        }

        private SymbolsTable _globalTable;
        public SymbolsTable GlobalTable
        {
            get { return _globalTable; }
        }

        #endregion

        #region Public Methods

        public bool Validate(out Code code)
        {
            _typesTable = new TypesTable();
            _globalTable = new SymbolsTable();
            _semanticErrors = new List<SemanticError>();

            this.MoveNext();
            Code pCode;
            var program = Program(out pCode);
            code = pCode;

            return program && _end;
        }

        #endregion

        #region Private Methods

        private bool Program(out Code code)
        {
            Code dsCode;
            if (DS(out dsCode))
            {
                code = dsCode;
                return true;
            }

            code = string.Empty;
            return false;
        }

        private bool DS(out Code code)
        {
            if (_current.Type == TokenType.None)
            {
                code = string.Empty;
                return true;
            }

            if (ModifierOrEmpty())
            {
                if (_current.Type == TokenType.Const)
                {
                    this.MoveNext();
                    Code gcdCode;
                    if (GlobalConstantDeclaration(out gcdCode))
                    {
                        Code dsCode;
                        if (DS(out dsCode))
                        {
                            code = new Code();
                            code.Append(gcdCode);
                            code.Append(dsCode);
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                Code gdCode;
                if (GlobalDeclaration(out gdCode))
                {
                    Code dsCode;
                    if (DS(out dsCode))
                    {
                        code = new Code();
                        code.Append(gdCode);
                        code.Append(dsCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool GlobalDeclaration(out Code code)
        {
            if (_current.Type == TokenType.Void)
            {
                this.MoveNext();
                if (_current.Type == TokenType.Identifier)
                {
                    var identifier = _current.Lexeme;
                    this.MoveNext();
                    if (_current.Type == TokenType.OpenParentheses)
                    {
                        this.MoveNext();
                        Code fdCode;
                        if (FunctionDeclaration(identifier, _typesTable.GetFromName(TokenType.Void.ToString()), out fdCode))
                        {
                            code = fdCode;
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            Type type;
            if (Type(out type))
            {
                if (_current.Type == TokenType.Identifier)
                {
                    var token = _current;

                    this.MoveNext();
                    if (_current.Type == TokenType.OpenParentheses)
                    {
                        this.MoveNext();
                        Code fdCode;
                        if (FunctionDeclaration(token.Lexeme, type, out fdCode))
                        {
                            code = fdCode;
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    var s = new Symbol(token.Lexeme, type);
                    this.AddGlobalSymbol(s, token);

                    Code viCode;
                    Type viType;
                    if (VariableInitialization(_globalTable, s, out viType, out viCode))
                    {
                        Code vCode;
                        if (V(type, _globalTable, out vCode))
                        {
                            if (_current.Type == TokenType.Semicolon)
                            {
                                this.MoveNext();
                                code = new Code();
                                code.Append(viCode);
                                code.Append(vCode);
                                return true;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool GlobalConstantDeclaration(out Code code)
        {
            if (_current.Type == TokenType.Void)
            {
                this.MoveNext();
                if (_current.Type == TokenType.Identifier)
                {
                    var identifier = _current.Lexeme;
                    this.MoveNext();
                    if (_current.Type == TokenType.OpenParentheses)
                    {
                        this.MoveNext();
                        Code fdCode;
                        if (FunctionDeclaration(identifier, _typesTable.GetFromName(TokenType.Void.ToString()), out fdCode))
                        {
                            code = fdCode;
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            Type type;
            if (Type(out type))
            {
                if (_current.Type == TokenType.Identifier)
                {
                    var token = _current;

                    this.MoveNext();
                    if (_current.Type == TokenType.OpenParentheses)
                    {
                        this.MoveNext();
                        Code fdCode;
                        if (FunctionDeclaration(token.Lexeme, type, out fdCode))
                        {
                            code = fdCode;
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    var s = new Symbol(token.Lexeme, type);
                    this.AddGlobalSymbol(s, token);

                    Code ciCode;
                    if (ConstantInitialization(out ciCode))
                    {
                        Code vCode;
                        if (V(type, _globalTable, out vCode))
                        {
                            if (_current.Type == TokenType.Semicolon)
                            {
                                this.MoveNext();
                                code = new Code();
                                code.Append(ciCode);
                                code.Append(vCode);
                                return true;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool V(Type type, SymbolsTable symbolsTable, out Code code)
        {
            Code idsCode;
            if (IdentifierDeclarationsSetLine(type, symbolsTable, out idsCode))
            {
                code = idsCode;
                return true;
            }

            code = string.Empty;
            return false;
        }

        private bool Modifier()
        {
            if (_end)
                return false;

            if (_current.Type == TokenType.Static || _current.Type == TokenType.Volatile)
            {
                this.MoveNext();
                return true;
            }

            return false;
        }

        private bool ModifierOrEmpty()
        {
            if (Modifier())
                return true;

            return true;
        }

        private bool IdentifierDeclarationsSetLine(Type type, SymbolsTable symbolsTable, out Code code)
        {
            if (_current.Type == TokenType.Comma)
            {
                this.MoveNext();
                Code idCode;
                if (IdentifierDeclaration(type, symbolsTable, out idCode))
                {
                    Code idsCode;
                    if (IdentifierDeclarationsSetLine(type, symbolsTable, out idsCode))
                    {
                        code = new Code();
                        code.Append(idCode);
                        code.Append(idsCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool VariableInitialization(SymbolsTable symbolsTable, Symbol symbol, out Type type, out Code code)
        {
            if (!_end && _current.Type == TokenType.Assignment)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    type = eType;
                    code = new Code();
                    code.Append(eCode);
                    code.AppendLine(string.Format("{0} = {1}", symbol.Identifier, ePlace));
                    return true;
                }

                type = null;
                code = string.Empty;
                return false;
            }

            type = null;
            code = string.Empty;
            return true;
        }

        private bool IdentifierDeclaration(Type type, SymbolsTable symbolsTable, out Code code)
        {
            if (!_end && _current.Type == TokenType.Identifier)
            {
                Symbol s;
                if (symbolsTable == _globalTable)
                    s = new Symbol(_current.Lexeme, type);
                else
                    s = new Symbol(_current.Lexeme, this.CreateTemporaryVariable(), type);

                this.AddSymbol(symbolsTable, s);

                this.MoveNext();
                if (!_end && _current.Type == TokenType.Assignment)
                {
                    this.MoveNext();
                    Type eType;
                    Code eCode;
                    string ePlace;
                    if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                    {
                        code = eCode;
                        code.AppendLine(string.Format("{0} = {1}", s.Identifier, ePlace));
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return true;
            }

            code = string.Empty;
            return false;
        }

        private bool ConstantInitialization(out Code code)
        {
            if (_current.Type == TokenType.Assignment)
            {
                this.MoveNext();
                if (ConstantExpression0())
                {
                    code = "VALOR DA CONSTANTE";
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool FunctionDeclaration(string identifier, Type returnType, out Code code)
        {
            IEnumerable<Type> parametersTypes;
            SymbolsTable symbolsTable = new SymbolsTable() { Parent = _globalTable };

            if (ParametersSetOrEmpty(out parametersTypes, symbolsTable))
            {
                var parameters = symbolsTable.ToList();
                if (_current.Type == TokenType.CloseParentheses)
                {
                    var symbol = new FunctionSymbol(identifier, returnType, parametersTypes);
                    this.AddGlobalSymbol(symbol);
                    this.MoveNext();

                    if (_current.Type == TokenType.Semicolon)
                    {
                        this.MoveNext();
                        code = new Code();
                        code.AppendLine(string.Format("{0}:", symbol.Identifier));
                        for (int i = 0; i < parameters.Count; i++)
                            code.AppendLine(string.Format("{0} = param[{1}]", parameters[i].Identifier, i));

                        if (symbol.Type == _typesTable.GetFromName(TokenType.Void.ToString()))
                            code.AppendLine("return");
                        else
                            code.AppendLine("return 1");

                        return true;
                    }

                    if (_current.Type == TokenType.OpenBraces)
                    {
                        this.MoveNext();

                        Code cCode;
                        if (CommandsSet(symbolsTable, symbol, string.Empty, string.Empty, out cCode))
                        {
                            if (_current.Type == TokenType.CloseBraces)
                            {
                                this.MoveNext();

                                code = new Code();
                                code.AppendLine(string.Format("{0}:", symbol.Identifier));
                                for (int i = 0; i < parameters.Count; i++)
                                    code.AppendLine(string.Format("{0} = param[{1}]", parameters[i].Identifier, i));
                                code.Append(cCode);
                                return true;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            parametersTypes = null;
            code = string.Empty;
            return false;
        }

        private bool Type(out Type type)
        {
            type = null;

            if (_end)
                return false;

            if (_current.Type == TokenType.Unsigned)
            {
                this.MoveNext();
                if (_current.Type == TokenType.Int ||
                    _current.Type == TokenType.Long)
                {
                    type = _typesTable.GetFromName(string.Format("{0} {1}", TokenType.Unsigned.ToString(), _current.Type.ToString()));
                    this.MoveNext();
                    return true;
                }
                return false;
            }

            if (_current.Type == TokenType.Byte ||
                _current.Type == TokenType.Int ||
                _current.Type == TokenType.Word ||
                _current.Type == TokenType.Short ||
                _current.Type == TokenType.Long ||
                _current.Type == TokenType.Float ||
                _current.Type == TokenType.Double ||
                _current.Type == TokenType.Boolean)
            {
                type = _typesTable.GetFromName(_current.Type.ToString());
                this.MoveNext();
                return true;
            }

            return false;
        }

        private bool ParametersSetOrEmpty(out IEnumerable<Type> parametersTypes, SymbolsTable symbolsTable)
        {
            parametersTypes = null;

            if (_end)
                return true;

            var temp1 = _currentIndex;
            if (ParametersSet(out parametersTypes, symbolsTable))
                return true;

            this.SetCurrent(temp1);

            return true;
        }

        private bool ParametersSet(out IEnumerable<Type> parametersTypes, SymbolsTable symbolsTable)
        {
            parametersTypes = null;

            if (_end)
                return false;

            var parametersTypesList = new List<Type>();
            parametersTypes = parametersTypesList;

            Type type;
            if (Parameter(out type, symbolsTable))
            {
                parametersTypesList.Add(type);

                if (_end)
                    return false;

                if (ParameterSetLine(parametersTypesList, symbolsTable))
                    return true;

                return false;
            }

            return false;
        }

        private bool ParameterSetLine(List<Type> parametersTypesList, SymbolsTable symbolsTable)
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.Comma)
            {
                this.MoveNext();
                if (_end)
                    return false;

                Type type;
                if (Parameter(out type, symbolsTable))
                {
                    parametersTypesList.Add(type);
                    if (ParameterSetLine(parametersTypesList, symbolsTable))
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool Parameter(out Type type, SymbolsTable symbolsTable)
        {
            type = null;

            if (_end)
                return false;

            if (Type(out type))
            {
                if (_current.Type == TokenType.Identifier)
                {
                    this.AddSymbol(symbolsTable, new Symbol(_current.Lexeme, this.CreateTemporaryVariable(), type));
                    this.MoveNext();
                    return true;
                }
            }

            return false;
        }

        private bool Command(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            Type type1, type2;

            if (_current.Type == TokenType.OpenBraces)
            {
                this.MoveNext();
                Code csCode;
                if (CommandsSet(symbolsTable, functionSymbol, labelBreak, labelContinue, out csCode))
                {
                    if (_current.Type == TokenType.CloseBraces)
                    {
                        code = csCode;
                        this.MoveNext();
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.For)
            {
                this.MoveNext();
                Code fCode;
                if (For(symbolsTable, functionSymbol, out fCode))
                {
                    code = fCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.While)
            {
                this.MoveNext();

                Code wCode;
                if (While(symbolsTable, functionSymbol, out wCode))
                {
                    code = wCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Do)
            {
                this.MoveNext();

                Code wdCode;
                if (WhileDo(symbolsTable, functionSymbol, out wdCode))
                {
                    code = wdCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Switch)
            {
                this.MoveNext();

                Code sCode;
                if (Switch(symbolsTable, functionSymbol, labelBreak, labelContinue, out sCode))
                {
                    code = sCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.If)
            {
                this.MoveNext();

                Code iCode;
                if (If(symbolsTable, functionSymbol, labelBreak, labelContinue, out iCode))
                {
                    code = iCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.GoTo)
            {
                this.MoveNext();
                if (_current.Type == TokenType.Identifier)
                {
                    this.MoveNext();
                    if (_current.Type == TokenType.Semicolon)
                    {
                        this.MoveNext();
                        code = "GERAR CODIGO DO GOTO";
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Break)
            {
                var breakToken = _current;
                this.MoveNext();
                if (_current.Type == TokenType.Semicolon)
                {
                    this.MoveNext();

                    if (string.IsNullOrEmpty(labelBreak))
                    {
                        code = string.Empty;
                        _semanticErrors.Add(new SemanticError("O break está fora de um laço.", breakToken));
                    }
                    else
                        code = string.Format("goto {0}", labelBreak);

                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Continue)
            {
                var continueToken = _current;
                this.MoveNext();
                if (_current.Type == TokenType.Semicolon)
                {
                    this.MoveNext();

                    if (string.IsNullOrEmpty(labelContinue))
                    {
                        code = string.Empty;
                        _semanticErrors.Add(new SemanticError("O continue está fora de um laço.", continueToken));
                    }
                    else
                        code = string.Format("goto {0}", labelContinue);

                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Return)
            {
                this.MoveNext();

                Code rCode;
                if (Return(symbolsTable, functionSymbol, out rCode))
                {
                    code = rCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Semicolon)
            {
                this.MoveNext();
                code = string.Empty;
                return true;
            }

            Type eType;
            Code eCode;
            string ePlace;
            if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
            {
                if (_current.Type == TokenType.Semicolon)
                {
                    this.MoveNext();
                    code = eCode;
                    return true;
                }
                code = string.Empty;
                return false;
            }

            if (ModifierOrEmpty())
            {
                if (_current.Type == TokenType.Const)
                {
                    this.MoveNext();

                    if (Type(out type1))
                    {
                        Code cv2Code;
                        if (CV2(type1, symbolsTable, out cv2Code))
                        {
                            if (_current.Type == TokenType.Semicolon)
                            {
                                this.MoveNext();
                                code = cv2Code;
                                return true;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                if (Type(out type2))
                {
                    Code v2Code;
                    if (V2(type2, symbolsTable, out v2Code))
                    {
                        if (_current.Type == TokenType.Semicolon)
                        {
                            code = v2Code;
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool V2(Type type, SymbolsTable symbolsTable, out Code code)
        {
            if (_current.Type == TokenType.Identifier)
            {
                Symbol s;
                if (symbolsTable == _globalTable)
                    s = new Symbol(_current.Lexeme, type);
                else
                    s = new Symbol(_current.Lexeme, this.CreateTemporaryVariable(), type);
                this.AddSymbol(symbolsTable, s);

                this.MoveNext();
                Code viCode;
                Type viType;
                if (VariableInitialization(symbolsTable, s, out viType, out viCode))
                {
                    Code vCode;
                    if (V(type, symbolsTable, out vCode))
                    {
                        code = new Code();
                        code.Append(viCode);
                        code.Append(vCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool CV2(Type type, SymbolsTable symbolsTable, out Code code)
        {
            if (_current.Type == TokenType.Identifier)
            {
                var s = new Symbol(_current.Lexeme, this.CreateTemporaryVariable(), type);
                this.AddSymbol(symbolsTable, s);

                this.MoveNext();
                Code ciCode;
                if (ConstantInitialization(out ciCode))
                {
                    Code vCode;
                    if (V(type, symbolsTable, out vCode))
                    {
                        code = new Code();
                        code.Append(ciCode);
                        code.Append(vCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool CommandsSet(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.For ||
                _current.Type == TokenType.While ||
                _current.Type == TokenType.Do ||
                _current.Type == TokenType.Switch ||
                _current.Type == TokenType.If ||
                _current.Type == TokenType.Identifier ||
                _current.Type == TokenType.Increment ||
                _current.Type == TokenType.Decrement ||
                _current.Type == TokenType.GoTo ||
                _current.Type == TokenType.Break ||
                _current.Type == TokenType.Continue ||
                _current.Type == TokenType.Return ||
                _current.Type == TokenType.Semicolon ||
                _current.Type == TokenType.Static ||
                _current.Type == TokenType.Volatile ||
                _current.Type == TokenType.Const ||
                _current.Type == TokenType.Unsigned ||
                _current.Type == TokenType.Byte ||
                _current.Type == TokenType.Int ||
                _current.Type == TokenType.Word ||
                _current.Type == TokenType.Short ||
                _current.Type == TokenType.Long ||
                _current.Type == TokenType.Float ||
                _current.Type == TokenType.Double ||
                _current.Type == TokenType.Boolean ||
                _current.Type == TokenType.True ||
                _current.Type == TokenType.False ||
                _current.Type == TokenType.OpenParentheses ||
                _current.Type == TokenType.IntegerConstant ||
                _current.Type == TokenType.RealConstant ||
                _current.Type == TokenType.Subtraction ||
                _current.Type == TokenType.Addition ||
                _current.Type == TokenType.Not)
            {
                Code cCode;
                if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                {
                    Code csCode;
                    if (CommandsSet(symbolsTable, functionSymbol, labelBreak, labelContinue, out csCode))
                    {
                        code = new Code();
                        code.Append(cCode);
                        code.Append(csCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool For(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, out Code code)
        {
            var sb = new StringBuilder();

            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();
                Code fssCode;
                if (ForStatementSet(symbolsTable, out fssCode))
                {
                    if (_current.Type == TokenType.Semicolon)
                    {
                        this.MoveNext();

                        if (_current.Type == TokenType.Semicolon)
                        {
                            this.MoveNext();
                            Code fssCode2;
                            if (ForStatementSet(symbolsTable, out fssCode2))
                            {
                                if (_current.Type == TokenType.CloseParentheses)
                                {
                                    this.MoveNext();

                                    var labelEnd = this.CreateLabel();
                                    var labelContinue = this.CreateLabel();
                                    Code cCode;
                                    if (Command(symbolsTable, functionSymbol, labelEnd, labelContinue, out cCode))
                                    {
                                        var labelLoop = this.CreateLabel();

                                        code = new Code();
                                        code.Append(fssCode);
                                        code.AppendLine(string.Format("{0}:", labelLoop));
                                        code.Append(cCode);
                                        code.AppendLine(string.Format("{0}:", labelContinue));
                                        code.Append(fssCode2);
                                        code.Append(string.Format("goto {0}", labelLoop));
                                        code.AppendLine(string.Format("{0}:", labelEnd));
                                        return true;
                                    }

                                    code = string.Empty;
                                    return false;
                                }

                                code = string.Empty;
                                return false;
                            }

                            code = string.Empty;
                            return false;
                        }

                        Type eType;
                        Code eCode;
                        string ePlace;
                        if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                        {
                            if (_current.Type == TokenType.Semicolon)
                            {
                                this.MoveNext();
                                Code fssCode2;
                                if (ForStatementSet(symbolsTable, out fssCode2))
                                {
                                    if (_current.Type == TokenType.CloseParentheses)
                                    {
                                        this.MoveNext();

                                        var labelEnd = this.CreateLabel();
                                        var labelContinue = this.CreateLabel();
                                        Code cCode;
                                        if (Command(symbolsTable, functionSymbol, labelEnd, labelContinue, out cCode))
                                        {
                                            var labelLoop = this.CreateLabel();

                                            code = new Code();
                                            code.Append(fssCode);
                                            code.AppendLine(string.Format("{0}:", labelLoop));
                                            code.Append(eCode);
                                            code.AppendLine(string.Format("if {0} = 0 goto {1}", ePlace, labelEnd));
                                            code.Append(cCode);
                                            code.AppendLine(string.Format("{0}:", labelContinue));
                                            code.Append(fssCode2);
                                            code.Append(string.Format("goto {0}", labelLoop));
                                            code.AppendLine(string.Format("{0}:", labelEnd));
                                            return true;
                                        }

                                        code = string.Empty;
                                        return false;
                                    }

                                    code = string.Empty;
                                    return false;
                                }

                                code = string.Empty;
                                return false;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool While(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, out Code code)
        {
            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    if (_current.Type == TokenType.CloseParentheses)
                    {
                        this.MoveNext();

                        var labelEnd = this.CreateLabel();
                        var labelLoop = this.CreateLabel();
                        Code cCode;
                        if (Command(symbolsTable, functionSymbol, labelEnd, labelLoop, out cCode))
                        {
                            code = new Code();
                            code.AppendLine(string.Format("{0}:", labelLoop));
                            code.Append(eCode);
                            code.AppendLine(string.Format("if {0} = 0 goto {1}", ePlace, labelEnd));
                            code.Append(cCode);
                            code.AppendLine(string.Format("goto {0}", labelLoop));
                            code.AppendLine(string.Format("{0}:", labelEnd));
                            return true;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool WhileDo(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, out Code code)
        {
            var labelEnd = this.CreateLabel();
            var labelContinue = this.CreateLabel();
            Code cCode;
            if (Command(symbolsTable, functionSymbol, labelEnd, labelContinue, out cCode))
            {
                if (_current.Type == TokenType.While)
                {
                    this.MoveNext();
                    if (_current.Type == TokenType.OpenParentheses)
                    {
                        this.MoveNext();

                        Type eType;
                        Code eCode;
                        string ePlace;
                        if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                        {
                            if (_current.Type == TokenType.CloseParentheses)
                            {
                                this.MoveNext();
                                if (_current.Type == TokenType.Semicolon)
                                {
                                    var labelLoop = this.CreateLabel();
                                    code = new Code();
                                    code.AppendLine(string.Format("{0}:", labelLoop));
                                    code.Append(cCode);
                                    code.AppendLine(string.Format("{0}:", labelContinue));
                                    code.Append(eCode);
                                    code.AppendLine(string.Format("if {0} = 1 goto {1}", ePlace, labelLoop));
                                    code.AppendLine(string.Format("{0}:", labelEnd));
                                    this.MoveNext();
                                    return true;
                                }

                                code = string.Empty;
                                return false;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool Switch(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    if (_current.Type == TokenType.CloseParentheses)
                    {
                        this.MoveNext();
                        if (_current.Type == TokenType.OpenBraces)
                        {
                            this.MoveNext();

                            Code csCode;
                            if (CasesSet(symbolsTable, functionSymbol, labelBreak, labelContinue, out csCode))
                            {
                                if (_current.Type == TokenType.CloseBraces)
                                {
                                    this.MoveNext();

                                    code = "GERACAO DO CODIGO DO SWITCH";
                                    code.Append(eCode);
                                    code.Append(csCode);
                                    return true;
                                }

                                code = string.Empty;
                                return false;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool CasesSet(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            Code cCode;
            if (_current.Type == TokenType.Case)
            {
                this.MoveNext();

                if (Case(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                {
                    Code csCode;
                    if (CasesSet(symbolsTable, functionSymbol, labelBreak, labelContinue, out csCode))
                    {
                        code = new Code();
                        code.Append(cCode);
                        code.Append(csCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Default)
            {
                this.MoveNext();

                Code dCode;
                if (Default(symbolsTable, functionSymbol, labelBreak, labelContinue, out dCode))
                {
                    Code cslCode;
                    if (CasesSetLine(symbolsTable, functionSymbol, labelBreak, labelContinue, out cslCode))
                    {
                        code = new Code();
                        code.Append(dCode);
                        code.Append(cslCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
            {
                Code csCode;
                if (CasesSet(symbolsTable, functionSymbol, labelBreak, labelContinue, out csCode))
                {
                    code = new Code();
                    code.Append(cCode);
                    code.Append(csCode);
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool CasesSetLine(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.Case)
            {
                this.MoveNext();
                Code cslCode;
                if (CasesSetLine(symbolsTable, functionSymbol, labelBreak, labelContinue, out cslCode))
                {
                    code = cslCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            Code cCode;
            if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
            {
                Code cslCode;
                if (CasesSetLine(symbolsTable, functionSymbol, labelBreak, labelContinue, out cslCode))
                {
                    code = new Code();
                    code.Append(cCode);
                    code.Append(cslCode);
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool Case(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (ConstantExpression0())
            {
                if (_current.Type == TokenType.Colon)
                {
                    this.MoveNext();

                    Code cCode;
                    if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                    {
                        code = cCode;
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool Default(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.Colon)
            {
                this.MoveNext();

                Code cCode;
                if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                {
                    code = cCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool If(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    if (_current.Type == TokenType.CloseParentheses)
                    {
                        this.MoveNext();
                        Code cCode;
                        if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                        {
                            Code elCode;
                            if (Else(symbolsTable, functionSymbol, labelBreak, labelContinue, out elCode))
                            {
                                var labelElse = this.CreateLabel();
                                var labelEnd = this.CreateLabel();

                                code = new Code();
                                code.Append(eCode);
                                code.AppendLine(string.Format("if {0} = 0 goto {1}", ePlace, labelElse));
                                code.Append(cCode);
                                code.AppendLine(string.Format("goto {0}", labelEnd));
                                code.AppendLine(string.Format("{0}:", labelElse));
                                code.Append(elCode);
                                code.AppendLine(string.Format("{0}:", labelEnd));

                                return true;
                            }

                            code = string.Empty;
                            return false;
                        }

                        code = string.Empty;
                        return false;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        private bool Else(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, string labelBreak, string labelContinue, out Code code)
        {
            if (_current.Type == TokenType.Else)
            {
                this.MoveNext();

                Code cCode;
                if (Command(symbolsTable, functionSymbol, labelBreak, labelContinue, out cCode))
                {
                    code = cCode;
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool ForStatementSet(SymbolsTable symbolsTable, out Code code)
        {
            if (_current.Type == TokenType.Static ||
                _current.Type == TokenType.Volatile ||
                _current.Type == TokenType.Const ||
                _current.Type == TokenType.Unsigned ||
                _current.Type == TokenType.Byte ||
                _current.Type == TokenType.Int ||
                _current.Type == TokenType.Word ||
                _current.Type == TokenType.Short ||
                _current.Type == TokenType.Long ||
                _current.Type == TokenType.Float ||
                _current.Type == TokenType.Double ||
                _current.Type == TokenType.Boolean)
            {
                Type tType;
                if (Type(out tType))
                {
                    Code v2Code;
                    if (V2(tType, symbolsTable, out v2Code))
                    {
                        code = v2Code;
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            Type eType;
            Code eCode;
            string ePlace;
            if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
            {
                Code fsslCode;
                if (ForStatementSetLine(symbolsTable, out fsslCode))
                {
                    code = new Code();
                    code.Append(eCode);
                    code.Append(fsslCode);
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        //private bool V3(Type type, SymbolsTable symbolsTable, out Code code)
        //{
        //    if (_current.Type == TokenType.Identifier)
        //    {
        //        var s = new Symbol(_current.Lexeme, type);
        //        this.AddSymbol(symbolsTable, s);

        //        this.MoveNext();
        //        Code viCode;
        //        if (VariableInitialization(symbolsTable, s, out viCode))
        //        {
        //            Code idslCode;
        //            if (IdentifierDeclarationsSetLine(type, symbolsTable, out idslCode))
        //            {
        //                code = new Code();
        //                code.Append(viCode);
        //                code.Append(idslCode);
        //                return true;
        //            }

        //            code = string.Empty;
        //            return false;
        //        }

        //        code = string.Empty;
        //        return false;
        //    }

        //    code = string.Empty;
        //    return false;
        //}

        //private bool CV3(Type type, SymbolsTable symbolsTable, out Code code)
        //{
        //    if (_current.Type == TokenType.Identifier)
        //    {
        //        this.MoveNext();
        //        Code ciCode;
        //        if (ConstantInitialization(out ciCode))
        //        {
        //            Code idslCode;
        //            if (IdentifierDeclarationsSetLine(type, symbolsTable, out idslCode))
        //            {
        //                code = new Code();
        //                code.Append(ciCode);
        //                code.Append(idslCode);
        //                return true;
        //            }

        //            code = string.Empty;
        //            return false;
        //        }

        //        code = string.Empty;
        //        return false;
        //    }

        //    code = string.Empty;
        //    return false;
        //}

        private bool ForStatementSetLine(SymbolsTable symbolsTable, out Code code)
        {
            if (_current.Type == TokenType.Comma)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    Code fsslCode;
                    if (ForStatementSetLine(symbolsTable, out fsslCode))
                    {
                        code = new Code();
                        code.Append(eCode);
                        code.Append(fsslCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool Arguments(SymbolsTable symbolsTable, ref int paramCount, out Code code)
        {
            Type eType;
            Code eCode;
            string ePlace;
            if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
            {
                Code alCode;
                if (ArgumentsLine(symbolsTable, ref paramCount, out alCode))
                {
                    paramCount++;
                    code = new Code();
                    code.Append(eCode);
                    code.AppendLine(string.Format("param {0}", ePlace));
                    code.Append(alCode);
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool ArgumentsLine(SymbolsTable symbolsTable, ref int paramCount, out Code code)
        {
            if (_current.Type == TokenType.Comma)
            {
                this.MoveNext();

                Type eType;
                Code eCode;
                string ePlace;
                if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
                {
                    Code alCode;
                    if (ArgumentsLine(symbolsTable, ref paramCount, out alCode))
                    {
                        paramCount++;
                        code = new Code();
                        code.Append(eCode);
                        code.AppendLine(string.Format("param {0}", ePlace));
                        code.Append(alCode);
                        return true;
                    }

                    code = string.Empty;
                    return false;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return true;
        }

        private bool Return(SymbolsTable symbolsTable, FunctionSymbol functionSymbol, out Code code)
        {
            if (_current.Type == TokenType.Semicolon)
            {
                var token = _current;
                this.MoveNext();

                if (functionSymbol.Type != _typesTable.GetFromName(TokenType.Void.ToString()))
                    _semanticErrors.Add(new SemanticError(string.Format("Senteça de {0} sem valor para função de tipo {1}", TokenType.Return.ToString(), functionSymbol.Type.ToString()), token));

                code = "return";
                return true;
            }

            Type eType;
            Code eCode;
            string ePlace;
            if (Expression0(symbolsTable, out eType, out eCode, out ePlace))
            {
                if (_current.Type == TokenType.Semicolon)
                {
                    var token = _current;
                    this.MoveNext();

                    if (functionSymbol.Type == _typesTable.GetFromName(TokenType.Void.ToString()))
                        _semanticErrors.Add(new SemanticError(string.Format("Senteça de {0} com valor para função de tipo {1}", TokenType.Return.ToString(), functionSymbol.Type.ToString()), token));

                    code = new Code();
                    code.Append(eCode);
                    code.AppendLine(string.Format("return {0}", ePlace));
                    return true;
                }

                code = string.Empty;
                return false;
            }

            code = string.Empty;
            return false;
        }

        #region Expressions

        private bool Expression0(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e1Type;
            Code e1Code;
            string e1Place;
            if (Expression1(symbolsTable, out e1Type, out e1Code, out e1Place))
            {
                if (_current.Type == TokenType.Assignment ||
                    _current.Type == TokenType.AdditionAssignment ||
                    _current.Type == TokenType.SubtractionAssignment ||
                    _current.Type == TokenType.MultiplicationAssignment ||
                    _current.Type == TokenType.DivisionAssignment ||
                    _current.Type == TokenType.DivisionRemainderAssignment ||
                    _current.Type == TokenType.ShiftLeftAssignment ||
                    _current.Type == TokenType.ShiftRightAssignment ||
                    _current.Type == TokenType.BitwiseAndAssignment ||
                    _current.Type == TokenType.BitwiseOrAssignment ||
                    _current.Type == TokenType.BitwiseXorAssignment)
                {
                    var token = _current;
                    this.MoveNext();

                    Type e0Type;
                    Code e0Code;
                    string e0Place;
                    if (Expression0(symbolsTable, out e0Type, out e0Code, out e0Place))
                    {
                        var symbol = symbolsTable.GetSymbolFromTemporary(e1Place);
                        if (symbol == null)
                            _semanticErrors.Add(new SemanticError("lvalue esperado como operador esquerdo da atribuição.", _current));

                        type = this.CheckType(token, e1Type, e0Type);
                        place = e1Place;

                        string operationChar;
                        switch (token.Type)
                        {
                            case TokenType.AdditionAssignment: operationChar = "+"; break;
                            case TokenType.SubtractionAssignment: operationChar = "-"; break;
                            case TokenType.MultiplicationAssignment: operationChar = "*"; break;
                            case TokenType.DivisionAssignment: operationChar = "/"; break;
                            case TokenType.DivisionRemainderAssignment: operationChar = "%"; break;
                            case TokenType.ShiftLeftAssignment: operationChar = "<<"; break;
                            case TokenType.ShiftRightAssignment: operationChar = ">>"; break;
                            case TokenType.BitwiseAndAssignment: operationChar = "&"; break;
                            case TokenType.BitwiseOrAssignment: operationChar = "|"; break;
                            default:
                            case TokenType.BitwiseXorAssignment: operationChar = "^"; break;
                        }

                        code = new Code();
                        code.Append(e1Code);
                        code.Append(e0Code);

                        if (token.Type == TokenType.Assignment)
                            code.AppendLine(string.Format("{0} = {1}", e1Place, e0Place));
                        else
                            code.AppendLine(string.Format("{0} = {0} {1} {2}", e1Place, operationChar, e0Place));

                        return true;
                    }

                    type = null;
                    code = string.Empty;
                    place = string.Empty;
                    return false;
                }

                type = e1Type;
                place = e1Place;
                code = e1Code;
                return true;
            }

            type = null;
            code = string.Empty;
            place = string.Empty;
            return false;
        }

        private bool Expression1(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e2Type;
            Code e2Code;
            string e2Place;
            if (Expression2(symbolsTable, out e2Type, out e2Code, out e2Place))
            {
                Type re1hType = e2Type;
                Code re1hCode = e2Code;
                string re1hPlace = e2Place;

                Type re1sType;
                Code re1sCode;
                string re1sPlace;
                if (ExpressionRE1(symbolsTable, re1hType, re1hCode, re1hPlace, out re1sType, out re1sCode, out re1sPlace))
                {
                    type = re1sType;
                    code = re1sCode;
                    place = re1sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE1(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.Or)
            {
                var token = _current;
                this.MoveNext();
                Type e2Type;
                Code e2Code;
                string e2Place;
                if (Expression2(symbolsTable, out e2Type, out e2Code, out e2Place))
                {
                    var temp = this.CreateTemporaryVariable();
                    Type re1hType = this.CheckType(token, hType, e2Type);
                    Code re1hCode = new Code();
                    re1hCode.Append(hCode);
                    re1hCode.Append(e2Code);
                    re1hCode.AppendLine(string.Format("{0} = {1} || {2}", temp, hPlace, e2Place));
                    string re1hPlace = temp;
                    Type re1sType;
                    Code re1sCode;
                    string re1sPlace;

                    if (ExpressionRE1(symbolsTable, re1hType, re1hCode, re1hPlace, out re1sType, out re1sCode, out re1sPlace))
                    {
                        sType = re1sType;
                        sPlace = re1sPlace;
                        sCode = re1sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression2(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e3Type;
            Code e3Code;
            string e3Place;
            if (Expression3(symbolsTable, out e3Type, out e3Code, out e3Place))
            {
                Type re2hType = e3Type;
                Code re2hCode = e3Code;
                string re2hPlace = e3Place;

                Type re2sType;
                Code re2sCode;
                string re2sPlace;
                if (ExpressionRE2(symbolsTable, re2hType, re2hCode, re2hPlace, out re2sType, out re2sCode, out re2sPlace))
                {
                    type = re2sType;
                    code = re2sCode;
                    place = re2sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE2(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.And)
            {
                var token = _current;
                this.MoveNext();
                Type e3Type;
                Code e3Code;
                string e3Place;
                if (Expression3(symbolsTable, out e3Type, out e3Code, out e3Place))
                {
                    var temp = this.CreateTemporaryVariable();
                    Type re2hType = this.CheckType(token, hType, e3Type);
                    Code re2hCode = new Code();
                    re2hCode.Append(hCode);
                    re2hCode.Append(e3Code);
                    re2hCode.AppendLine(string.Format("{0} = {1} && {2}", temp, hPlace, e3Place));
                    string re2hPlace = temp;
                    Type re2sType;
                    Code re2sCode;
                    string re2sPlace;

                    if (ExpressionRE2(symbolsTable, re2hType, re2hCode, re2hPlace, out re2sType, out re2sCode, out re2sPlace))
                    {
                        sType = re2sType;
                        sPlace = re2sPlace;
                        sCode = re2sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression3(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e4Type;
            Code e4Code;
            string e4Place;
            if (Expression4(symbolsTable, out e4Type, out e4Code, out e4Place))
            {
                Type re3hType = e4Type;
                Code re3hCode = e4Code;
                string re3hPlace = e4Place;

                Type re3sType;
                Code re3sCode;
                string re3sPlace;
                if (ExpressionRE3(symbolsTable, re3hType, re3hCode, re3hPlace, out re3sType, out re3sCode, out re3sPlace))
                {
                    type = re3sType;
                    code = re3sCode;
                    place = re3sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE3(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.BitwiseOr)
            {
                var token = _current;
                this.MoveNext();
                Type e4Type;
                Code e4Code;
                string e4Place;
                if (Expression4(symbolsTable, out e4Type, out e4Code, out e4Place))
                {
                    var temp = this.CreateTemporaryVariable();
                    Type re3hType = this.CheckType(token, hType, e4Type);
                    Code re3hCode = new Code();
                    re3hCode.Append(hCode);
                    re3hCode.Append(e4Code);
                    re3hCode.AppendLine(string.Format("{0} = {1} | {2}", temp, hPlace, e4Place));
                    string re3hPlace = temp;
                    Type re3sType;
                    Code re3sCode;
                    string re3sPlace;

                    if (ExpressionRE3(symbolsTable, re3hType, re3hCode, re3hPlace, out re3sType, out re3sCode, out re3sPlace))
                    {
                        sType = re3sType;
                        sPlace = re3sPlace;
                        sCode = re3sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression4(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e5Type;
            Code e5Code;
            string e5Place;
            if (Expression5(symbolsTable, out e5Type, out e5Code, out e5Place))
            {
                Type re4hType = e5Type;
                Code re4hCode = e5Code;
                string re4hPlace = e5Place;

                Type re4sType;
                Code re4sCode;
                string re4sPlace;
                if (ExpressionRE4(symbolsTable, re4hType, re4hCode, re4hPlace, out re4sType, out re4sCode, out re4sPlace))
                {
                    type = re4sType;
                    code = re4sCode;
                    place = re4sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE4(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.BitwiseXor)
            {
                var token = _current;
                this.MoveNext();
                Type e5Type;
                Code e5Code;
                string e5Place;
                if (Expression5(symbolsTable, out e5Type, out e5Code, out e5Place))
                {
                    var temp = this.CreateTemporaryVariable();
                    Type re4hType = this.CheckType(token, hType, e5Type);
                    Code re4hCode = new Code();
                    re4hCode.Append(hCode);
                    re4hCode.Append(e5Code);
                    re4hCode.AppendLine(string.Format("{0} = {1} ^ {2}", temp, hPlace, e5Place));
                    string re4hPlace = temp;
                    Type re4sType;
                    Code re4sCode;
                    string re4sPlace;

                    if (ExpressionRE4(symbolsTable, re4hType, re4hCode, re4hPlace, out re4sType, out re4sCode, out re4sPlace))
                    {
                        sType = re4sType;
                        sPlace = re4sPlace;
                        sCode = re4sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression5(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e6Type;
            Code e6Code;
            string e6Place;
            if (Expression6(symbolsTable, out e6Type, out e6Code, out e6Place))
            {
                Type re5hType = e6Type;
                Code re5hCode = e6Code;
                string re5hPlace = e6Place;

                Type re5sType;
                Code re5sCode;
                string re5sPlace;
                if (ExpressionRE5(symbolsTable, re5hType, re5hCode, re5hPlace, out re5sType, out re5sCode, out re5sPlace))
                {
                    type = re5sType;
                    code = re5sCode;
                    place = re5sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE5(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.BitwiseAnd)
            {
                var token = _current;
                this.MoveNext();
                Type e6Type;
                Code e6Code;
                string e6Place;
                if (Expression6(symbolsTable, out e6Type, out e6Code, out e6Place))
                {
                    var temp = this.CreateTemporaryVariable();
                    Type re5hType = this.CheckType(token, hType, e6Type);
                    Code re5hCode = new Code();
                    re5hCode.Append(hCode);
                    re5hCode.Append(e6Code);
                    re5hCode.AppendLine(string.Format("{0} = {1} & {2}", temp, hPlace, e6Place));
                    string re5hPlace = temp;
                    Type re5sType;
                    Code re5sCode;
                    string re5sPlace;

                    if (ExpressionRE5(symbolsTable, re5hType, re5hCode, re5hPlace, out re5sType, out re5sCode, out re5sPlace))
                    {
                        sType = re5sType;
                        sPlace = re5sPlace;
                        sCode = re5sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression6(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e7Type;
            Code e7Code;
            string e7Place;
            if (Expression7(symbolsTable, out e7Type, out e7Code, out e7Place))
            {
                Type re8hType = e7Type;
                Code re8hCode = e7Code;
                string re8hPlace = e7Place;

                Type re6sType;
                Code re6sCode;
                string re6sPlace;
                if (ExpressionRE6(symbolsTable, re8hType, re8hCode, re8hPlace, out re6sType, out re6sCode, out re6sPlace))
                {
                    type = re6sType;
                    code = re6sCode;
                    place = re6sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE6(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.Equality ||
                _current.Type == TokenType.Inequality)
            {
                var token = _current;
                this.MoveNext();
                Type e7Type;
                Code e7Code;
                string e7Place;
                if (Expression7(symbolsTable, out e7Type, out e7Code, out e7Place))
                {
                    var operationChar = token.Type == TokenType.Equality ? "==" : "!=";

                    var temp = this.CreateTemporaryVariable();
                    Type re6hType = this.CheckType(token, hType, e7Type);
                    Code re6hCode = new Code();
                    re6hCode.Append(hCode);
                    re6hCode.Append(e7Code);
                    re6hCode.AppendLine(string.Format("{0} = {1} {2} {3}", temp, hPlace, operationChar, e7Place));
                    string re6hPlace = temp;
                    Type re6sType;
                    Code re6sCode;
                    string re6sPlace;

                    if (ExpressionRE6(symbolsTable, re6hType, re6hCode, re6hPlace, out re6sType, out re6sCode, out re6sPlace))
                    {
                        sType = re6sType;
                        sPlace = re6sPlace;
                        sCode = re6sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression7(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e8Type;
            Code e8Code;
            string e8Place;
            if (Expression8(symbolsTable, out e8Type, out e8Code, out e8Place))
            {
                Type re7hType = e8Type;
                Code re7hCode = e8Code;
                string re7hPlace = e8Place;

                Type re7sType;
                Code re7sCode;
                string re7sPlace;
                if (ExpressionRE7(symbolsTable, re7hType, re7hCode, re7hPlace, out re7sType, out re7sCode, out re7sPlace))
                {
                    type = re7sType;
                    code = re7sCode;
                    place = re7sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE7(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.LessThan ||
                _current.Type == TokenType.GreaterThan ||
                _current.Type == TokenType.LessThanOrEqual ||
                _current.Type == TokenType.GreaterThanOrEqual)
            {
                var token = _current;
                this.MoveNext();
                Type e8Type;
                Code e8Code;
                string e8Place;
                if (Expression8(symbolsTable, out e8Type, out e8Code, out e8Place))
                {
                    var operationChar = token.Type == TokenType.Addition ? "+" : "-";
                    switch (token.Type)
                    {
                        case TokenType.LessThan: operationChar = "<"; break;
                        case TokenType.GreaterThan: operationChar = ">"; break;
                        case TokenType.LessThanOrEqual: operationChar = "<="; break;
                        default:
                        case TokenType.GreaterThanOrEqual: operationChar = ">="; break;
                    }

                    var temp = this.CreateTemporaryVariable();
                    Type re7hType = this.CheckType(token, hType, e8Type);
                    Code re7hCode = new Code();
                    re7hCode.Append(hCode);
                    re7hCode.Append(e8Code);
                    re7hCode.AppendLine(string.Format("{0} = {1} {2} {3}", temp, hPlace, operationChar, e8Place));
                    string re7hPlace = temp;
                    Type re7sType;
                    Code re7sCode;
                    string re7sPlace;

                    if (ExpressionRE7(symbolsTable, re7hType, re7hCode, re7hPlace, out re7sType, out re7sCode, out re7sPlace))
                    {
                        sType = re7sType;
                        sPlace = re7sPlace;
                        sCode = re7sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression8(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e9Type;
            Code e9Code;
            string e9Place;
            if (Expression9(symbolsTable, out e9Type, out e9Code, out e9Place))
            {
                Type re8hType = e9Type;
                Code re8hCode = e9Code;
                string re8hPlace = e9Place;

                Type re8sType;
                Code re8sCode;
                string re8sPlace;
                if (ExpressionRE8(symbolsTable, re8hType, re8hCode, re8hPlace, out re8sType, out re8sCode, out re8sPlace))
                {
                    type = re8sType;
                    code = re8sCode;
                    place = re8sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE8(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.ShiftLeft ||
                _current.Type == TokenType.ShiftRight)
            {
                var token = _current;
                this.MoveNext();
                Type e9Type;
                Code e9Code;
                string e9Place;
                if (Expression9(symbolsTable, out e9Type, out e9Code, out e9Place))
                {
                    var operationChar = token.Type == TokenType.ShiftLeft ? "<<" : ">>";

                    var temp = this.CreateTemporaryVariable();
                    Type re8hType = this.CheckType(token, hType, e9Type);
                    Code re8hCode = new Code();
                    re8hCode.Append(hCode);
                    re8hCode.Append(e9Code);
                    re8hCode.AppendLine(string.Format("{0} = {1} {2} {3}", temp, hPlace, operationChar, e9Place));
                    string re8hPlace = temp;
                    Type re8sType;
                    Code re8sCode;
                    string re8sPlace;

                    if (ExpressionRE8(symbolsTable, re8hType, re8hCode, re8hPlace, out re8sType, out re8sCode, out re8sPlace))
                    {
                        sType = re8sType;
                        sPlace = re8sPlace;
                        sCode = re8sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression9(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e10Type;
            Code e10Code;
            string e10Place;
            if (Expression10(symbolsTable, out e10Type, out e10Code, out e10Place))
            {
                Type re9hType = e10Type;
                Code re9hCode = e10Code;
                string re9hPlace = e10Place;

                Type re9sType;
                Code re9sCode;
                string re9sPlace;
                if (ExpressionRE9(symbolsTable, re9hType, re9hCode, re9hPlace, out re9sType, out re9sCode, out re9sPlace))
                {
                    type = re9sType;
                    code = re9sCode;
                    place = re9sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE9(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.Addition ||
                _current.Type == TokenType.Subtraction)
            {
                var token = _current;
                this.MoveNext();
                Type e10Type;
                Code e10Code;
                string e10Place;
                if (Expression10(symbolsTable, out e10Type, out e10Code, out e10Place))
                {
                    var operationChar = token.Type == TokenType.Addition ? "+" : "-";

                    var temp = this.CreateTemporaryVariable();
                    Type re9hType = this.CheckType(token, hType, e10Type);
                    Code re9hCode = new Code();
                    re9hCode.Append(hCode);
                    re9hCode.Append(e10Code);
                    re9hCode.AppendLine(string.Format("{0} = {1} {2} {3}", temp, hPlace, operationChar, e10Place));
                    string re9hPlace = temp;
                    Type re9sType;
                    Code re9sCode;
                    string re9sPlace;

                    if (ExpressionRE9(symbolsTable, re9hType, re9hCode, re9hPlace, out re9sType, out re9sCode, out re9sPlace))
                    {
                        sType = re9sType;
                        sPlace = re9sPlace;
                        sCode = re9sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression10(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Type e11Type;
            Code e11Code;
            string e11Place;
            if (Expression11(symbolsTable, out e11Type, out e11Code, out e11Place))
            {
                Type re10hType = e11Type;
                Code re10hCode = e11Code;
                string re10hPlace = e11Place;

                Type re10sType;
                Code re10sCode;
                string re10sPlace;
                if (ExpressionRE10(symbolsTable, re10hType, re10hCode, re10hPlace, out re10sType, out re10sCode, out re10sPlace))
                {
                    type = re10sType;
                    code = re10sCode;
                    place = re10sPlace;
                    return true;
                }
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        private bool ExpressionRE10(SymbolsTable symbolsTable, Type hType, Code hCode, string hPlace, out Type sType, out Code sCode, out string sPlace)
        {
            if (_current.Type == TokenType.Multiplication ||
                _current.Type == TokenType.Division ||
                _current.Type == TokenType.DivisionRemainder)
            {
                var token = _current;
                this.MoveNext();
                Type e11Type;
                Code e11Code;
                string e11Place;
                if (Expression11(symbolsTable, out e11Type, out e11Code, out e11Place))
                {
                    var operationChar = token.Type == TokenType.Multiplication ? "*" : token.Type == TokenType.Division ? "/" : "%";

                    var temp = this.CreateTemporaryVariable();
                    Type re10hType = this.CheckType(token, hType, e11Type);
                    Code re10hCode = new Code();
                    re10hCode.Append(hCode);
                    re10hCode.Append(e11Code);
                    re10hCode.AppendLine(string.Format("{0} = {1} {2} {3}", temp, hPlace, operationChar, e11Place));
                    string re10hPlace = temp;
                    Type re10sType;
                    Code re10sCode;
                    string re10sPlace;

                    if (ExpressionRE10(symbolsTable, re10hType, re10hCode, re10hPlace, out re10sType, out re10sCode, out re10sPlace))
                    {
                        sType = re10sType;
                        sPlace = re10sPlace;
                        sCode = re10sCode;
                        return true;
                    }

                    sType = null;
                    sPlace = string.Empty;
                    sCode = string.Empty;
                    return false;
                }

                sType = null;
                sPlace = string.Empty;
                sCode = string.Empty;
                return false;
            }

            sType = hType;
            sPlace = hPlace;
            sCode = hCode;
            return true;
        }

        private bool Expression11(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Code codel;
            string placel;

            if (_current.Type == TokenType.Addition ||
                _current.Type == TokenType.Subtraction ||
                _current.Type == TokenType.Not)
            {
                var token = _current;

                this.MoveNext();
                if (Expression11(symbolsTable, out type, out codel, out placel))
                {
                    if (token.Type == TokenType.Addition)
                    {
                        code = codel;
                        place = placel;
                    }
                    else
                    {
                        var temp = this.CreateTemporaryVariable();
                        place = temp;
                        code = new Code();
                        code.Append(codel);
                        code.AppendLine(string.Format("{0} = {1}{2}", temp, token.Type == TokenType.Subtraction ? "-" : "!", placel));
                    }

                    return true;
                }

                code = string.Empty;
                place = string.Empty;
                return false;
            }

            if (_current.Type == TokenType.Increment ||
                _current.Type == TokenType.Decrement)
            {
                var token = _current;

                this.MoveNext();
                if (Expression12(symbolsTable, out type, out codel, out placel))
                {
                    var symbol = symbolsTable.GetSymbolFromTemporary(placel);
                    if (symbol == null)
                        _semanticErrors.Add(new SemanticError("lvalue esperado como operador esquerdo da atribuição.", _current));

                    place = placel;
                    code = new Code();
                    code.Append(codel);
                    code.AppendLine(string.Format("{0} = {0} {1} 1", placel, token.Type == TokenType.Increment ? "+" : "-"));

                    return true;
                }

                code = string.Empty;
                place = string.Empty;
                return false;
            }

            if (Expression12(symbolsTable, out type, out code, out place))
                return true;

            code = string.Empty;
            place = string.Empty;
            return false;
        }

        private bool Expression12(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            Code codel;
            string placel;
            if (ExpressionTerm(symbolsTable, out type, out codel, out placel))
            {
                if (_current.Type == TokenType.Increment ||
                    _current.Type == TokenType.Decrement)
                {
                    var symbol = symbolsTable.GetSymbolFromTemporary(placel);
                    if (symbol == null)
                        _semanticErrors.Add(new SemanticError("lvalue esperado como operador esquerdo da atribuição.", _current));

                    var temp = this.CreateTemporaryVariable();
                    place = temp;
                    code = new Code();
                    code.Append(codel);
                    code.AppendLine(string.Format("{0} = {1}", temp, placel));
                    code.AppendLine(string.Format("{0} = {0} {1} 1", placel, _current.Type == TokenType.Increment ? "+" : "-"));

                    this.MoveNext();
                    return true;
                }
                code = codel;
                place = placel;

                return true;
            }

            code = string.Empty;
            place = string.Empty;
            return false;
        }

        private bool ExpressionTerm(SymbolsTable symbolsTable, out Type type, out Code code, out string place)
        {
            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();

                if (Expression0(symbolsTable, out type, out code, out place))
                {
                    if (_current.Type == TokenType.CloseParentheses)
                    {
                        this.MoveNext();
                        return true;
                    }

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.IntegerConstant)
            {
                var value = int.Parse(_current.Lexeme);
                this.MoveNext();
                type = _typesTable.GetFromName(TokenType.Int.ToString());
                place = this.CreateTemporaryVariable();
                code = string.Format("{0} = {1}", place, value);
                return true;
            }

            if (_current.Type == TokenType.RealConstant)
            {
                var value = float.Parse(_current.Lexeme, CultureInfo.InvariantCulture);
                this.MoveNext();
                type = _typesTable.GetFromName(TokenType.Float.ToString());
                place = this.CreateTemporaryVariable();
                code = string.Format("{0} = {1}", place, value);
                return true;
            }

            if (_current.Type == TokenType.True)
            {
                this.MoveNext();
                type = _typesTable.GetFromName(TokenType.Int.ToString());
                place = this.CreateTemporaryVariable();
                code = string.Format("{0} = {1}", place, 1);
                return true;
            }

            if (_current.Type == TokenType.False)
            {
                this.MoveNext();
                type = _typesTable.GetFromName(TokenType.Int.ToString());
                place = this.CreateTemporaryVariable();
                code = string.Format("{0} = {1}", place, 0);
                return true;
            }

            if (_current.Type == TokenType.Identifier)
            {
                var symbolToken = _current;
                var symbol = this.ExistsSymbol(symbolsTable, _current.Lexeme);
                this.MoveNext();

                if (_current.Type == TokenType.OpenParentheses)
                {
                    this.MoveNext();
                    Code aCode;
                    int paramCount = 0;
                    if (Arguments(symbolsTable, ref paramCount, out aCode))
                    {
                        if (_current.Type == TokenType.CloseParentheses)
                        {
                            this.MoveNext();

                            var functionSymbol = symbol as FunctionSymbol;
                            if (functionSymbol != null)
                            {
                                var functionParamCount = functionSymbol.ParametersTypes.Count();
                                if (paramCount == functionParamCount)
                                {
                                    code = new Code();
                                    code.Append(aCode);
                                    if (functionSymbol.Type == _typesTable.GetFromName(TokenType.Void.ToString()))
                                    {
                                        place = "ERROR";
                                        code.AppendLine(string.Format("call {0}, {1}", functionSymbol.OriginalIdentifier, functionParamCount));
                                    }
                                    else
                                    {
                                        place = this.CreateTemporaryVariable();
                                        code.AppendLine(string.Format("{0} = call {1}, {2}", place, functionSymbol.OriginalIdentifier, functionParamCount));
                                    }
                                    type = functionSymbol.Type;
                                }
                                else
                                {
                                    _semanticErrors.Add(new SemanticError("Número de argumentos incompatível com a função.", symbolToken));
                                    code = string.Empty;
                                    type = null;
                                    place = "ERROR";
                                }
                            }
                            else
                            {
                                if (symbol != null)
                                    _semanticErrors.Add(new SemanticError(string.Format("'{0}' não pode ser utilizada como uma função", symbol.Identifier), symbolToken));

                                code = string.Empty;
                                type = null;
                                place = "ERROR";
                            }
                            return true;
                        }

                        type = null;
                        place = string.Empty;
                        code = string.Empty;
                        return false;
                    }

                    type = null;
                    place = string.Empty;
                    code = string.Empty;
                    return false;
                }

                if (symbol == null)
                {
                    type = null;
                    place = "ERROR";
                }
                else
                {
                    type = symbol.Type;
                    place = symbol.Identifier;
                }
                code = string.Empty;
                return true;
            }

            type = null;
            place = string.Empty;
            code = string.Empty;
            return false;
        }

        #endregion

        #region ConstantExpressions

        private bool ConstantExpression0()
        {
            if (_end)
                return false;

            if (ConstantExpression1())
            {
                if (_current.Type == TokenType.Assignment ||
                    _current.Type == TokenType.AdditionAssignment ||
                    _current.Type == TokenType.SubtractionAssignment ||
                    _current.Type == TokenType.MultiplicationAssignment ||
                    _current.Type == TokenType.DivisionAssignment ||
                    _current.Type == TokenType.DivisionRemainderAssignment ||
                    _current.Type == TokenType.ShiftLeftAssignment ||
                    _current.Type == TokenType.ShiftRightAssignment ||
                    _current.Type == TokenType.BitwiseAndAssignment ||
                    _current.Type == TokenType.BitwiseOrAssignment ||
                    _current.Type == TokenType.BitwiseXorAssignment)
                {
                    this.MoveNext();
                    if (ConstantExpression0())
                        return true;
                }

                return true;
            }

            return false;
        }

        private bool ConstantExpression1()
        {
            if (_end)
                return false;

            if (ConstantExpression2())
            {
                if (ConstantExpression1B())
                    return true;

                return false;
            }

            return false;
        }

        private bool ConstantExpression1B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.Or)
            {
                this.MoveNext();
                if (ConstantExpression2())
                {
                    if (ConstantExpression1B())
                        return true;

                    return false;
                }
                return false;
            }

            return true;
        }

        private bool ConstantExpression2()
        {
            if (_end)
                return false;

            if (ConstantExpression3())
            {
                if (ConstantExpression2B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression2B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.And)
            {
                this.MoveNext();
                if (ConstantExpression3())
                {
                    if (ConstantExpression2B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression3()
        {
            if (_end)
                return false;

            if (ConstantExpression4())
            {
                if (ConstantExpression3B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression3B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.BitwiseOr)
            {
                this.MoveNext();
                if (ConstantExpression4())
                {
                    if (ConstantExpression3B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression4()
        {
            if (_end)
                return false;

            if (ConstantExpression5())
            {
                if (ConstantExpression4B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression4B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.BitwiseXor)
            {
                this.MoveNext();
                if (ConstantExpression5())
                {
                    if (ConstantExpression4B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression5()
        {
            if (_end)
                return false;

            if (ConstantExpression6())
            {
                if (ConstantExpression5B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression5B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.BitwiseAnd)
            {
                this.MoveNext();
                if (ConstantExpression6())
                {
                    if (ConstantExpression5B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression6()
        {
            if (_end)
                return false;

            if (ConstantExpression7())
            {
                if (ConstantExpression6B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression6B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.Equality)
            {
                this.MoveNext();
                if (ConstantExpression7())
                {
                    if (ConstantExpression6B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.Inequality)
            {
                this.MoveNext();
                if (ConstantExpression7())
                {
                    if (ConstantExpression6B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression7()
        {
            if (_end)
                return false;

            if (ConstantExpression8())
            {
                if (ConstantExpression7B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression7B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.LessThan)
            {
                this.MoveNext();
                if (ConstantExpression8())
                {
                    if (ConstantExpression7B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.GreaterThan)
            {
                this.MoveNext();
                if (ConstantExpression8())
                {
                    if (ConstantExpression7B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.LessThanOrEqual)
            {
                this.MoveNext();
                if (ConstantExpression8())
                {
                    if (ConstantExpression7B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.GreaterThanOrEqual)
            {
                this.MoveNext();
                if (ConstantExpression8())
                {
                    if (ConstantExpression7B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression8()
        {
            if (_end)
                return false;

            if (ConstantExpression9())
            {
                if (ConstantExpression8B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression8B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.ShiftLeft)
            {
                this.MoveNext();
                if (ConstantExpression9())
                {
                    if (ConstantExpression8B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.ShiftRight)
            {
                this.MoveNext();
                if (ConstantExpression9())
                {
                    if (ConstantExpression8B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression9()
        {
            if (_end)
                return false;

            if (ConstantExpression10())
            {
                if (ConstantExpression9B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression9B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.Addition)
            {
                this.MoveNext();
                if (ConstantExpression10())
                {
                    if (ConstantExpression9B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.Subtraction)
            {
                this.MoveNext();
                if (ConstantExpression10())
                {
                    if (ConstantExpression9B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression10()
        {
            if (_end)
                return false;

            if (ConstantExpression11())
            {
                if (ConstantExpression10B())
                    return true;
            }

            return false;
        }

        private bool ConstantExpression10B()
        {
            if (_end)
                return true;

            if (_current.Type == TokenType.Multiplication)
            {
                this.MoveNext();
                if (ConstantExpression11())
                {
                    if (ConstantExpression10B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.Division)
            {
                this.MoveNext();
                if (ConstantExpression11())
                {
                    if (ConstantExpression10B())
                        return true;

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.DivisionRemainder)
            {
                this.MoveNext();
                if (ConstantExpression11())
                {
                    if (ConstantExpression10B())
                        return true;

                    return false;
                }

                return false;
            }

            return true;
        }

        private bool ConstantExpression11()
        {
            if (_end)
                return false;

            if (_current.Type == TokenType.Addition)
            {
                this.MoveNext();
                //if (ConstantExpression12())
                //    return true;
                if (ConstantExpressionSet())
                    return true;
            }

            //if (_current.Type == TokenType.Increment)
            //{
            //    this.MoveNext();
            //    if (ConstantExpression12())
            //        return true;
            //}

            if (_current.Type == TokenType.Subtraction)
            {
                this.MoveNext();
                //if (ConstantExpression12())
                //    return true;
                if (ConstantExpressionSet())
                    return true;
            }

            //if (_current.Type == TokenType.Decrement)
            //{
            //    this.MoveNext();
            //    if (ConstantExpression12())
            //        return true;
            //}

            //if (ConstantExpression12())
            //    return true;

            if (ConstantExpressionSet())
                return true;

            return false;
        }

        private bool ConstantExpression12()
        {
            if (_end)
                return false;

            if (ConstantExpressionSet())
            {
                if (_current.Type == TokenType.Increment)
                {
                    this.MoveNext();
                    return true;
                }

                if (_current.Type == TokenType.Decrement)
                {
                    this.MoveNext();
                    return true;
                }

                return true;
            }

            return false;
        }

        private bool ConstantExpressionSet()
        {
            if (_end)
                return false;

            if (_current.Type == TokenType.OpenParentheses)
            {
                this.MoveNext();
                if (ConstantExpression0())
                {
                    if (_current.Type == TokenType.CloseParentheses)
                    {
                        this.MoveNext();
                        return true;
                    }

                    return false;
                }

                return false;
            }

            if (_current.Type == TokenType.IntegerConstant ||
                _current.Type == TokenType.RealConstant)
            {
                this.MoveNext();
                return true;
            }

            return false;
        }

        #endregion

        private void MoveNext()
        {
            if (_currentIndex + 1 >= _tokens.Count)
            {
                try
                {
                    Token token;
                    do
                    {
                        token = _enumerator.GetNext();
                    }
                    while (token != null && token.Type == TokenType.Comment);

                    if (token == null)
                    {
                        _end = true;
                        _current = new Token(TokenType.None, string.Empty);
                    }
                    else
                    {
                        _tokens.Add(token);
                        _currentIndex++;
                    }
                }
                catch (LexicalErrorException)
                {
                    _end = true;
                    _current = new Token(TokenType.None, string.Empty);
                }
            }
            else
                _currentIndex++;

            if (!_end)
                _current = _tokens[_currentIndex];
        }

        private void SetCurrent(int index)
        {
            _currentIndex = index;
            _current = _tokens[index];
        }

        private void AddGlobalSymbol(Symbol symbol)
        {
            this.AddGlobalSymbol(symbol, _current);
        }

        private void AddGlobalSymbol(Symbol symbol, Token token)
        {
            this.AddSymbol(_globalTable, symbol, token);
        }

        private void AddSymbol(SymbolsTable symbolsTable, Symbol symbol)
        {
            this.AddSymbol(symbolsTable, symbol, _current);
        }

        private void AddSymbol(SymbolsTable symbolsTable, Symbol symbol, Token token)
        {
            if (symbolsTable.Exists(s => s.OriginalIdentifier == symbol.OriginalIdentifier))
                _semanticErrors.Add(new SemanticError("Variáveis com conflito de identificador.", token));

            symbolsTable.Add(symbol);
        }

        private Symbol ExistsSymbol(SymbolsTable symbolsTable, string lexeme)
        {
            var symbol = symbolsTable.GetSymbolFromOriginal(lexeme);
            if (symbol == null)
                _semanticErrors.Add(new SemanticError(string.Format("Variável não declarada: {0}", lexeme), _current));

            return symbol;
        }

        private string CreateTemporaryVariable()
        {
            return string.Format("@T{0}", _tempCount++);
        }

        private string CreateLabel()
        {
            return string.Format("Lb{0}", _labelsCount++);
        }

        private Type CheckType(Token token, Type type1, Type type2)
        {
            var voidType = _typesTable.GetFromName(TokenType.Void.ToString());

            if (type1 == voidType || type2 == voidType)
            {
                _semanticErrors.Add(new SemanticError(string.Format("Utilização do tipo {0} como operando.", voidType.ToString()), token));
                return type1;
            }

            var floatType = _typesTable.GetFromName(TokenType.Float.ToString());
            var intType = _typesTable.GetFromName(TokenType.Int.ToString());

            switch (token.Type)
            {
                case TokenType.And:
                case TokenType.Or:
                case TokenType.Equality:
                case TokenType.Inequality:
                case TokenType.GreaterThan:
                case TokenType.LessThan:
                case TokenType.GreaterThanOrEqual:
                case TokenType.LessThanOrEqual:
                    return intType;
                case TokenType.BitwiseAnd:
                case TokenType.BitwiseXor:
                case TokenType.BitwiseOr:
                case TokenType.BitwiseAndAssignment:
                case TokenType.BitwiseXorAssignment:
                case TokenType.BitwiseOrAssignment:
                    if (type1 != intType || type2 != intType)
                        _semanticErrors.Add(new SemanticError(string.Format("Operandos inválidos de tipo {0} e {1} para a operação {2}.", type1.ToString(), type2.ToString(), token.Type), token));
                    return intType;
                case TokenType.Assignment:
                case TokenType.AdditionAssignment:
                case TokenType.SubtractionAssignment:
                case TokenType.MultiplicationAssignment:
                case TokenType.DivisionAssignment:
                case TokenType.DivisionRemainderAssignment:
                case TokenType.ShiftLeftAssignment:
                case TokenType.ShiftRightAssignment:
                    return type1;
                default:
                    if (type1 == floatType || type2 == floatType)
                        return floatType;
                    else
                        return intType;
            }
        }

        #endregion
    }
}